# --------------------------------------------------------------------

from petsc4py.PETSc import COMM_NULL
from petsc4py.PETSc import COMM_SELF
from petsc4py.PETSc import COMM_WORLD

# --------------------------------------------------------------------

from petsc4py.PETSc cimport MPI_Comm
from petsc4py.PETSc cimport PetscObject, PetscViewer
from petsc4py.PETSc cimport PetscIS
from petsc4py.PETSc cimport PetscVec, PetscMat
from petsc4py.PETSc cimport PetscKSP, PetscPC

from petsc4py.PETSc cimport Comm
from petsc4py.PETSc cimport Object, Viewer
from petsc4py.PETSc cimport IS
from petsc4py.PETSc cimport Vec, Mat
from petsc4py.PETSc cimport KSP, PC

# --------------------------------------------------------------------

cdef extern from *:
    ctypedef char const_char "const char"

cdef inline object bytes2str(const_char p[]):
     if p == NULL: 
         return None
     cdef bytes s = <char*>p
     if isinstance(s, str):
         return s
     else:
         return s.decode()

cdef inline object str2bytes(object s, const_char *p[]):
    if s is None:
        p[0] = NULL
        return None
    if not isinstance(s, bytes):
        s = s.encode()
    p[0] = <const_char*>(<char*>s)
    return s

cdef inline str S_(const_char p[]):
     if p == NULL: return None
     cdef bytes s = <char*>p
     return s if isinstance(s, str) else s.decode()

# --------------------------------------------------------------------

# Vile hack for raising a exception and not contaminating traceback

cdef extern from *:
    enum: PETSC_ERR_PYTHON "(-1)"

cdef extern from *:
    void PyErr_SetObject(object, object)
    void *PyExc_RuntimeError

cdef object PetscError = <object>PyExc_RuntimeError
from petsc4py.PETSc import Error as PetscError

cdef inline int SETERR(int ierr) with gil:
    if (<void*>PetscError) != NULL:
        PyErr_SetObject(PetscError, <long>ierr)
    else:
        PyErr_SetObject(<object>PyExc_RuntimeError, <long>ierr)
    return ierr

cdef inline int CHKERR(int ierr) nogil except -1:
    if ierr == 0:
        return 0  # no error
    if ierr == PETSC_ERR_PYTHON:
        return -1 # Python error
    SETERR(ierr)
    return -1

# --------------------------------------------------------------------

cdef extern from "compat.h": pass
cdef extern from "custom.h": pass

cdef extern from *:
    ctypedef long   PetscInt
    ctypedef double PetscReal
    ctypedef double PetscScalar

# --------------------------------------------------------------------

include "petsc.pxi"
include "taosys.pxi"
include "taosolver.pxi"

# --------------------------------------------------------------------

__doc__ = \
"""
Tools for Advanced Optimization.
"""

include "Solver.pyx"

def getVersion(patch=False, devel=False,
               date=False, author=False):
    cdef int cmajor = TAO_VERSION_MAJOR
    cdef int cminor = TAO_VERSION_MINOR
    cdef int cmicro = TAO_VERSION_SUBMINOR
    cdef int cpatch = TAO_VERSION_PATCH
    cdef int cdevel = not TAO_VERSION_RELEASE
    cdef const_char *cdate       = TAO_VERSION_DATE
    cdef const_char *cauthorinfo = TAO_AUTHOR_INFO
    version = (cmajor, cminor, cmicro)
    out = version
    if patch or devel or date or author:
        out = [version]
        if patch:
            out.append(cpatch)
        if devel:
            out.append(<bint>cdevel)
        if date:
            date = bytes2str(cdate)
            out.append(date)
        if author:
            author = bytes2str(cauthorinfo).split('\n')
            author = [s.strip() for s in author if s]
            out.append(author)
    return tuple(out)

@classmethod
def getVersionInfo():
    cdef int cmajor = TAO_VERSION_MAJOR
    cdef int cminor = TAO_VERSION_MINOR
    cdef int cmicro = TAO_VERSION_SUBMINOR
    cdef int cpatch = TAO_VERSION_PATCH
    cdef int crelease = TAO_VERSION_RELEASE
    cdef const_char *cdate       = TAO_VERSION_DATE
    cdef const_char *cauthorinfo = TAO_AUTHOR_INFO
    author = bytes2str(cauthorinfo).split('\n')
    author = [s.strip() for s in author if s]
    return dict(major      = cmajor,
                minor      = cminor,
                subminor   = cmicro,
                patch      = cpatch,
                release    = <bint>crelease,
                date       = bytes2str(cdate),
                patchdate  = bytes2str(cdate),
                authorinfo = author)

# --------------------------------------------------------------------

cdef extern from "Python.h":
    int Py_AtExit(void (*)())
    void PySys_WriteStderr(char*,...)

cdef extern from "stdio.h" nogil:
    ctypedef struct FILE
    FILE *stderr
    int fprintf(FILE *, char *, ...)

cdef int    PyTao_Argc
cdef char** PyTao_Argv
cdef char*  PyTao_Args[2]

PyTao_Args[0] = <char*>"tao4py"
PyTao_Args[1] = NULL

cdef int initialize(object args) except -1:
    if (<int>TaoInitializeCalled): return 1
    # initialize TAO
    global PyTao_Argc, PyTao_Argv, PyTao_Args
    PyTao_Argc = 1; PyTao_Argv = PyTao_Args
    CHKERR( TaoInitialize(&PyTao_Argc, &PyTao_Argv, NULL, NULL) )
    # register finalization function
    if Py_AtExit(finalize) < 0:
        PySys_WriteStderr("warning: could not register"
                          "TaoFinalize() with Py_AtExit()", 0)
    return 1 # and we are done, enjoy !!

from petsc4py.PETSc cimport PyPetscType_Register

cdef extern from *:
    ctypedef int PetscClassId
    PetscClassId TAOSOLVER_CLASSID
    int TaoInitializePackage(char[])

cdef int register(char path[]) except -1:
    # make sure all TAO components are initialized
    CHKERR( TaoInitializePackage(NULL) )
    # register tao4py Python types
    PyPetscType_Register(TAOSOLVER_CLASSID, Solver)
    return 0

cdef void finalize() nogil:
    # finalize TAO
    cdef int ierr = 0
    ierr = TaoFinalize()
    if ierr != 0:
        fprintf(stderr, "TaoFinalize() failed "
                "[error code: %d]\n", ierr)
    # and we are done, see you later !!

# --------------------------------------------------------------------

def _initialize(args=None):
    cdef int ready = initialize(args)
    if ready: register(NULL)

def _finalize():
    finalize()

# --------------------------------------------------------------------
